import 'dart:math';
import 'dart:typed_data';

import 'package:flutter/foundation.dart';

import 'enum.dart';

class CommandAPDU{

  Uint8List apdu;
  
  int _ne = 0;
  int _nc = 0;
  int _dataOffset = 0;
  
  CommandAPDU(this.apdu){
    parse();
  }

  /// Command APDU encoding options:
  /// <p>
  /// case 1:  |CLA|INS|P1 |P2 |                                 len = 4
  /// case 2s: |CLA|INS|P1 |P2 |LE |                             len = 5
  /// case 3s: |CLA|INS|P1 |P2 |LC |...BODY...|                  len = 6..260
  /// case 4s: |CLA|INS|P1 |P2 |LC |...BODY...|LE |              len = 7..261
  /// case 2e: |CLA|INS|P1 |P2 |00 |LE1|LE2|                     len = 7
  /// case 3e: |CLA|INS|P1 |P2 |00 |LC1|LC2|...BODY...|          len = 8..65542
  /// case 4e: |CLA|INS|P1 |P2 |00 |LC1|LC2|...BODY...|LE1|LE2|  len =10..65544
  /// <p>
  /// LE, LE1, LE2 may be 0x00.
  /// LC must not be 0x00 and LC1|LC2 must not be 0x00|0x00
  void parse() {
    if (apdu.length < 4) {
      throw("apdu must be at least 4 bytes long");
    }
    if (apdu.length == 4) {
      // case 1
      return;
    }
    int l1 = apdu[4] & 0xff;
    if (apdu.length == 5) {
      // case 2s
      _ne = (l1 == 0) ? 256 : l1;
      return;
    }
    if (l1 != 0) {
      if (apdu.length == 4 + 1 + l1) {
        // case 3s
        _nc = l1;
        _dataOffset = 5;
        return;
      } else if (apdu.length == 4 + 2 + l1) {
        // case 4s
        _nc = l1;
        _dataOffset = 5;
        int l2 = apdu[apdu.length - 1] & 0xff;
        _ne = (l2 == 0) ? 256 : l2;
        return;
      } else {
        throw
          ("Invalid APDU: length= ${apdu.length} + , b1= + l1");
      }
    }
    if (apdu.length < 7) {
      throw("Invalid APDU: length= ${apdu.length} + , b1= + l1");
    }
    int l2 = ((apdu[5] & 0xff) << 8) | (apdu[6] & 0xff);
    if (apdu.length == 7) {
      // case 2e
      _ne = (l2 == 0) ? 65536 : l2;
      return;
    }
    if (l2 == 0) {
      throw("Invalid APDU: length= ${apdu.length} + , b1=  $l1  , b2||b3=  $l2");
    }
    if (apdu.length == 4 + 3 + l2) {
      // case 3e
      _nc = l2;
      _dataOffset = 7;
      return;
    } else if (apdu.length == 4 + 5 + l2) {
      // case 4e
      _nc = l2;
      _dataOffset = 7;
      int leOfs = apdu.length - 2;
      int l3 = ((apdu[leOfs] & 0xff) << 8) | (apdu[leOfs + 1] & 0xff);
      _ne = (l3 == 0) ? 65536 : l3;
    } else {
      throw("Invalid APDU: length= ${apdu.length} + , b1=  $l1  , b2||b3=  $l2");
    }
  }

  /// APDU
  /// Constructs a CommandAPDU from the four header bytes. This is case 1
  /// in ISO 7816, no command body.
  /// cla  :1 Byte 0x00:指令标准 0xA2:聚元指令
  /// ins  :1 Byte 指令字节
  /// p1   :1 Byte  p1,p2:在所选文件中的偏移
  /// p2   :1 Byte
  /// lc   :1 Byte 数据字段的字节数
  /// data :ata to transmit.  Value of Lc field is set according to the data size
  /// le   :1 Byte 读入存储器的字节数  Response data length (Le) in bytes
  static Uint8List _apdu(int ins,int p1,int p2,{int cla =0xA2,int? lc,List<int>? data,int? le}){
    List<int> apdu = [cla,ins,p1,p2];
    if(lc != null){
      apdu.add(lc);
    }
    if(data != null){
      apdu.addAll(data);
    }

    if(le != null){
      apdu.add(le);
    }

    return Uint8List.fromList(apdu);
  }

  ///APDU select
  static Uint8List select(int p1,int p2,List<int> data,{int cla=0xA2,int ins=0xA4,int lc = 0x02})
  => _apdu(ins,p1,p2,cla: cla,data: data,lc: lc);

  ///APDU read
  static Uint8List readBinary(int p1,int p2,int le,{int cla=0xA2,int ins=0xB0,})
  => _apdu(ins,p1,p2,cla: cla,le: le,);

  ///APDU update
  static Uint8List updateBinary(int p1,int p2,int lc ,Uint8List data,{int cla=0xA2,int ins=0xD6})
  => _apdu(ins,p1,p2,cla: cla,data: data,lc: lc);

  ///随机8个字节
  static Uint8List createTrnd(){
    Uint8List trnd = Uint8List(8);
    for(int i = 0;i < 8;i++){
      trnd[i] = Random().nextInt(255);
    }
    return trnd;
  }

  /// access select指令
  ///
  static Uint8List accessSelect(){
    return select(0x00, 0x0C, [0xAC,0x01]);
  }

  /// data file select指令
  /// 01：加密 02：不加密-default
  static Uint8List dataFileSelect({NeedEncryption type = NeedEncryption.no}){
    return select(0x00, 0x0C, [0xDA,type.value]);
  }

  /// config file select指令
  /// 01：加密 02：不加密-default
  static Uint8List configFileSelect({NeedEncryption type = NeedEncryption.no}){
    return select(0x00, 0x0C, [0xCF,type.value]);
  }

  /// read access data
  /// TRND+MID
  /// dataType:0:User 1:admin
  static Uint8List readAccessData(AccessType type)
  =>readBinary(0x00,type.value,0x10);





}


